/*
 * Copyright (C) 2010 TopCoder Inc., All Rights Reserved.
 */
package com.hercules.xre.protocol.testing.netty.http;

import com.hercules.xre.protocol.testing.Helper;
import com.hercules.xre.protocol.testing.netty.IllegalMessageException;
import com.topcoder.util.log.Log;
import com.topcoder.util.log.LogManager;

import java.nio.charset.UnsupportedCharsetException;
import java.util.List;
import java.util.Map;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipelineCoverage;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.QueryStringDecoder;

/**
 * <p>
 * Channel handler for messages received trough HTTP connection. This handler should be proceeded in the pipeline by
 * HTTPRequestDecoder. This class retrieves POST parameters passed with a request and retrieves value of parameter that
 * should contain serialized events. Retrieved String value is then passed up in the pipeline to next message handler.
 * </p>
 * <p>
 * <strong>Thread-safety:</strong> This class is immutable and thus thread safe.
 * </p>
 *
 * @author TCSDESIGNER, TCSDEVELOPER
 * @version 1.0
 */
@ChannelPipelineCoverage("one")
public class HTTPRequestHandler extends SimpleChannelHandler {
    /**
     * <p>
     * Logger that is used to log activities and errors. It is initialized to
     * LogManager.getLog(HTTPRequestHandler.class.getName()). Can not be null, never changes after initialization.
     * </p>
     */
    private static final Log LOGGER = LogManager.getLog(HTTPRequestHandler.class.getName());

    /**
     * <p>
     * Holds name of HTTPRequest parameter that will hold serialized events. Can't be null, can't be empty.
     * </p>
     */
    private final String parameterName;

    /**
     * <p>
     * Creates new instance.
     * </p>
     *
     * @param parameterName name of request parameter that will hold XRE events, can't be null, can't be empty
     * @throws IllegalArgumentException when any of passed parameters is invalid
     */
    public HTTPRequestHandler(String parameterName) {
        final String signature = "HTTPRequestHandler#ctor";
        Helper.logEnter(signature, new String[] {"parameterName"}, new Object[] {parameterName}, LOGGER);
        try {
            Helper.checkString(parameterName, "parameterName", signature, LOGGER);
            this.parameterName = parameterName;
        } finally {
            Helper.logExit(signature, null, LOGGER);
        }
    }

    /**
     * <p>
     * Method called when new HTTPRequest has been received. It retrieves value of POST parameter that should contain
     * serialized events, this value is then passed to the next handler in pipeline.
     * </p>
     *
     * @param e message event, can't be null
     * @param ctx channel handler context, can't be null
     * @throws IllegalArgumentException when any of parameters is invalid.
     * @throws IllegalMessageException when passed message isn't an instance of HTTPRequest or request method isn't
     *             POST
     */
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws IllegalMessageException {
        final String signature = "HTTPRequestHandler#messageReceived";
        Helper.logEnter(signature, new String[] {"ChannelHandlerContext", "MessageEvent"}, new Object[] {ctx, e},
                LOGGER);
        try {
            Helper.checkNull(ctx, "ctx", signature, LOGGER);
            Helper.checkNull(e, "MessageEvent", signature, LOGGER);

            // get request content
            Object message = e.getMessage();
            if (!(message instanceof HttpRequest)) {
                throw Helper.logError(new IllegalMessageException("message must be instance of HttpRequest"),
                        signature, LOGGER);
            }
            HttpRequest request = (HttpRequest) message;
            // all requests should be passed by POST method, this needs to be checked
            if (request.getMethod() != HttpMethod.POST) {
                throw Helper.logError(new IllegalMessageException("the request must be passed by POST method"),
                        signature, LOGGER);
            }

            String requestContent = null;
            try {
                requestContent = request.getContent().toString("UTF-8");
            } catch (UnsupportedCharsetException ex) {
                throw Helper.logError(ex, signature, LOGGER);
            }
            // decode content
            QueryStringDecoder contentDecoder = new QueryStringDecoder("?" + requestContent);

            // get value of parameter that should contain serialized events
            Map<String, List<String>> parameters = contentDecoder.getParameters();

            List<String> events = parameters.get(parameterName);
            if (events == null || events.isEmpty()) {
                throw Helper.logError(
                        new IllegalMessageException("missing value for the parameter : " + parameterName), signature,
                        LOGGER);
            }
            // Note : the channel of ChannelHandlerContext can't be null, otherwise fireMessageReceived will throw NPE
            if (ctx.getChannel() == null) {
                throw Helper.logError(
                        new IllegalMessageException("the channel of ChannelHandlerContext can't be null"), signature,
                        LOGGER);
            }
            // pass retrieved events string representation to next handler in the pipeline
            Channels.fireMessageReceived(ctx, events.get(0));
        } finally {
            Helper.logExit(signature, null, LOGGER);
        }
    }
}
